/******************************************************************************

@File		: Sonant.c
@Author		: Sonant Synth by Jake Taylor
			: Windows Port by David Moloney, moloned@gmail.com
			; Realtime Sonant Synth by Conor Mac Aoidh, conormacaoidh@gmail.com
@Brief		: Realtime version of the Windows port of the Sonant Synth
Date		: 20/06/2012
Copyright	: (C) 2008-2009 Jake Taylor [ Ferris / Youth Uprising ]

This file contains the body of the Realtime Sonant Synth application.
In order to render a chunk of a song, call the Sonant_init function then
the Sonant_render function, which accepts a pointer to a short array.
The size of this array is defined in sonant.h

******************************************************************************/

/******************************************************************************
	1: Include files
******************************************************************************/

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include "sonant.h"

// header files to be synthesised
//#include "songs/attempt.h"
#include "songs/poseidon_demosong.h"
//#include "songs/ambidumbi.h"
//#include "songs/chill.h"
//#include "songs/haumeadrums.h"
//#include "songs/microscope.h"
//#include "songs/numan_cars.h"

/******************************************************************************
	2:  Source File Specific #defines and function prototypes
******************************************************************************/

#define AUDIO_CLIPAMP	32767							// audio clipping amplitude
#define MAX_DELAY		8 * _4K_SONANT_ROWLEN_ * 0.5	// maximum allowable delay (see sonant.h MAX_BUFF)
#define FRAC1			0.003921568627f					// 1/255.0f
#define FRAC2			0.001951219512f					// 1/512.0f
#define FRAC3			0.000071237925f					// 1/44100

// packs two shorts into an int
#define PACK( l, r ) \
	Sonant_clip( l ) << 16 | Sonant_clip( right )

// combines a left/right int with two shorts
#define COMBINE( x, l, r ) \
	x = PACK( ( ( x >> 16 ) + l ), ( ( x & 0xFFFF ) + r ) );

/******************************************************************************
	3:  Local variables
******************************************************************************/

unsigned int randseed = 1;
// Oscillator function-pointer type.
typedef float (*const oscillator_t)(float);

// Array of oscillator function-pointers for branchless linear access.
oscillator_t  afpOscillator[4] = {
	Sonant_osc_sin,
	Sonant_osc_square,
	Sonant_osc_saw,
	Sonant_osc_tri
};

typedef struct {
	// holds the current rendering position for this instrument
	unsigned currentpos;

	// the following hold variables which are calculated in the init function
	// and saved here rather than re-calculating each time
	unsigned max;
	unsigned d_len;
	float da;
	float q;
	float calc[ 10 ];
} inst_buffer;

// init structure to hold buffering position of instruments
inst_buffer render[ 8 ];

// init structure to hold buffer data
buff_data buffd[ 8 ][ _4K_SONANT_ENDPATTERN_ ][ 32 ];

// declare required variables
inst		*instrument;
inst_buffer	*buffer;
buff_data	*data;
float		rsample, r, f, high, t, lfor, e, left, right, c1, c2, low, band;
unsigned	g, b, currentpos, bufferpos, ind, i, cp, remain, pos, p, row, i_n, increment, maxpos, delind, delaycount = 0;
unsigned char n, reloaded;

/******************************************************************************
	4: Functions Implementation
******************************************************************************/

/******************************************************************************
@brief     : Initialises various variables used for each instrument      

@param     : void
@return    : void     
******************************************************************************/
void Sonant_init( void ){

	#ifdef DEBUG
		printf( "Sonant_init() initialising instruments\n" );
	#endif

	i = 8;
	do {
		#ifdef DEBUG
				printf( "instrument %d\n", i );
		#endif

		instrument			= &songdata.i[ --i ];
		buffer				= &render[ i ];

		buffer->currentpos	= 0;
		buffer->d_len		= instrument->fx_delay_time * _4K_SONANT_ROWLEN_ * 0.5;
		buffer->da			= (float) instrument->fx_delay_amt * FRAC1;
		buffer->q			= (float) instrument->fx_resonance * FRAC1;
		buffer->max			= instrument->env_attack + instrument->env_sustain + instrument->env_release;

		// carry out operations here for use later, rather than inside loop
		buffer->calc[ 0 ]	= (float) instrument->lfo_amt * FRAC2;
		buffer->calc[ 1 ]	= (instrument->osc1_oct - 8) * 12 + instrument->osc1_det;
		buffer->calc[ 2 ]	= 1.0f + .2f * (float) instrument->osc1_detune * FRAC1;
		buffer->calc[ 3 ]	= (float) instrument->osc1_vol * FRAC1;
		buffer->calc[ 4 ]	= (instrument->osc2_oct - 8) * 12 + instrument->osc2_det;
		buffer->calc[ 5 ]	= 1.0f + .2f * (float) instrument->osc2_detune * FRAC1;
		buffer->calc[ 6 ]	= (float) instrument->osc2_vol * FRAC1;
		buffer->calc[ 7 ]	= (float) instrument->noise_fader * FRAC1;
		buffer->calc[ 8 ]	= (float) instrument->fx_pan_amt * FRAC2;
		buffer->calc[ 9 ]	= (float) (instrument->env_master * 156);

		// zero data buffer
		b = _4K_SONANT_ENDPATTERN_;
		do{
			g = 32;
			--b;
			do{
				buffd[ i ][ b ][ --g ].i_n	= 0;
				buffd[ i ][ b ][ g ].c1	= 0;
				buffd[ i ][ b ][ g ].c2		= 0;
				buffd[ i ][ b ][ g ].low	= 0;
				buffd[ i ][ b ][ g ].band	= 0;
			} while( g );
		} while( b );

	} while( i );

}

/******************************************************************************

@brief     : Renders a chunk of audio to the buffer

@param     : wave_buffer - buffer to save rendered audio to                            
@return    : void     

******************************************************************************/
int Sonant_render( short* left_buffer, short* right_buffer ) {
	
	// zero buffer
	g = BUFF_SIZE;
	do{ left_buffer[ g ] = 0; right_buffer[ g ] = 0; } while( --g );

	#ifdef DEBUG
		printf( "Sonant_render( ): generating samples\n" );
	#endif

	// render song one instrument at a time
	i = 8;
	do{
		#ifdef DEBUG
				printf( "\n\ninstrument %d\n", i );
		#endif

		// initialise all required variables
		instrument	=	&songdata.i[--i];
		buffer		=	&render[ i ];
		bufferpos	=	ind	= currentpos = 0;
		reloaded	=	0;

		// loop through patterns
		for( p = 0; p < _4K_SONANT_ENDPATTERN_ - 1; ++p ){ // Patterns

			cp = instrument->p[ p ];

			for( row = 0; row < 32; ++row, currentpos += _4K_SONANT_ROWLEN_ ){ // Rows

				// buffer limit reached, next instrument
				if( bufferpos >= BUFF_SIZE )
					goto escape;

				n = instrument->c[ cp - 1 ].n[ row ];
				data = &buffd[ i ][ p ][ row ];

				// if already buffered, check there is nothing left over to do
				if( currentpos < buffer->currentpos ){

					maxpos = currentpos + buffer->max;
					//delind = buffer->d_len + currentpos;

					// check for left over sustain/attack/release
					if( maxpos > buffer->currentpos && data->i_n ){
						// rebuild buffering position
						printf( "resumed at p %d row %d\n", p, row );
						i_n = data->i_n;
						c1 = data->c1;
						c2 = data->c2;
						low = data->low;
						band = data->band;
						ind = 0;
					}
					// check for left over delay to apply
					//else if( buffer->d_len && delind > buffer->currentpos ){
					//	write = 0;
					//	i_n = buffer->currentpos;
					//}
					// nothing to do, skip
					else
						n = 0;
					increment = 0;
				}
				else{
					i_n = 0;
					increment = 1;
					c1 = c2 = low = band = 0;
					ind = bufferpos;
				}

				if( cp && n ){
					
					//c1	= c2 = low = band = 0;
					//ind	= i_n + bufferpos;			// current position in the buffer
					b	= i_n + currentpos;				// current overall position

					for( ; i_n < buffer->max; i_n++, ind++, b++ ){ // apply effects over period of ( attack + sustain + release )

						// save buffer data, stop processing
						if( ind > BUFF_SIZE ){
//							printf( "should resume at p %d row %d\n", p, row );
							data->i_n   = i_n;
							data->c1	= c1;
							data->c2	= c2;
							data->low	= low;
							data->band	= band;
							break;
						}

						// LFO
						t = Sonant_getFrequency( 1.0f, 2.0f, instrument->lfo_freq, 8 ) * (float) b / (float) _4K_SONANT_ROWLEN_;
						lfor = Sonant_getoscoutput( instrument->lfo_waveform, t ) * buffer->calc[ 0 ] + 0.5f;

						// Envelope
						e = 1.0f;
						if ( i_n < instrument->env_attack )
							e = ( float ) i_n / ( float ) instrument->env_attack;
						else if ( i_n >= instrument->env_attack + instrument->env_sustain )
							e -= ( float ) ( i_n - instrument->env_attack - instrument->env_sustain ) / ( float ) instrument->env_release;

						// Oscillator 1
						t = Sonant_getnotefreq( n + buffer->calc[ 1 ] ) * buffer->calc[ 2 ];
						if ( instrument->lfo_osc1_freq )
							t += lfor;
						if ( instrument->osc1_xenv )
							t *= e * e;
						c1 += t;
						r = ( float )Sonant_getoscoutput( instrument->osc1_waveform, c1 );
						rsample = r * buffer->calc[ 3 ];

						// Oscillator 2
						t = Sonant_getnotefreq( n + buffer->calc[ 4 ] ) * buffer->calc[ 5 ];
						if ( instrument->osc2_xenv )
							t *= e * e;
						c2 += t;
						r = ( float ) Sonant_getoscoutput( instrument->osc2_waveform, c2 );
						rsample += r * buffer->calc[ 6 ] +
							Sonant_osc_sin( Sonant_randfloat4k( ) ) * buffer->calc[ 7 ] * e; // Noise oscillator
						rsample *= e;

						// State variable filter
						f = instrument->fx_freq;
						if( instrument->lfo_fx_freq )
							f *= lfor;
						f = 1.5f * ( float ) sin( f * FRAC3 ); // 44.1kHz hardwired in sonant
						low += f * band;
						high = buffer->q * ( rsample - band ) - low;
						band += f * high;
						switch ( instrument->fx_filter ){
							case 1: rsample = high; break; // Highpass
							case 2: rsample = low; break; // Lopass
							case 3: rsample = band; break; // Bandpass
							case 4: rsample = low + high;  // Notch
						}
						t = Sonant_osc_sin( Sonant_getFrequency( 1.0f, 2.0f, instrument->fx_pan_freq, 8 ) * (float) b / (float) _4K_SONANT_ROWLEN_ )
							* buffer->calc[ 8 ] + 0.5f;
						rsample *= buffer->calc[ 9 ];

						// left and right data for this instrument
						left	=	rsample * ( 1.0f - t );
						right	=	rsample * t;

						// write with delay
						if( buffer->d_len ){
							delind = ind + buffer->d_len;

							if( delind < BUFF_SIZE ){ // delay is within boundaries of buffer
								left_buffer [ delind ] = Sonant_clip( left_buffer [ delind ] + ( left  * buffer->da ) );
								right_buffer[ delind ] = Sonant_clip( right_buffer[ delind ] + ( right * buffer->da ) );
							}
						}

						// write to buffer
						left_buffer [ ind ] = Sonant_clip( (int) left_buffer [ ind ] + left  );
						right_buffer[ ind ] = Sonant_clip( (int) right_buffer[ ind ] + right );

					} // for

				} // if( cp && n )

				if( increment )
					bufferpos += _4K_SONANT_ROWLEN_;

			} // Rows  

		} // Patterns

		#ifdef DEBUG
				printf( "finished all..\n" );
		#endif

		// finished buffering
		if( i == 0 )
			return 0;

		// goto marker
		escape:

		// save the current buffering position
		buffer->currentpos = currentpos;

	} while( i ); // one instrument at a time

	// still buffering
	return 1;
}

// Sonant_merge -> merges the left/right buffer to a single buffer
void Sonant_merge( short* wave_buffer, short* left_buffer, short* right_buffer ){
	int b = BUFF_SIZE, n = BUFF_SIZE * 2;
	do{
		wave_buffer[ --n ] = left_buffer[ --b ];
		wave_buffer[ --n ] = right_buffer[ b ];
	} while( b );
}

float Sonant_randfloat4k() {
	return (float) ( ( randseed *= 0x15a4e35 ) % 255 ) * FRAC1;
}


int Sonant_clip(int value) {
	if (value > AUDIO_CLIPAMP) return AUDIO_CLIPAMP;
	if (value < -AUDIO_CLIPAMP) return -AUDIO_CLIPAMP;
	return value;
}

// Oscillators
float Sonant_osc_sin(float value) {
	return (float)sin( value * 6.28318530F );
}


float Sonant_osc_square(float value) {
	if (Sonant_osc_sin(value) < 0) return -1.0f;
	else                    return 1.0f;
}


float Sonant_osc_saw(float value) {
	return (float)(fmod(value,1.0f) - 0.5f);
}


float Sonant_osc_tri(float value) {
	float v2;

	v2 = (Sonant_osc_saw(value) + .5f) * 4.0f;
	if (v2 < 2.0f) return v2 - 1.0f;
	else           return 3.0f - v2;
} 

float Sonant_getFrequency(float fFrequency, float fMultiplier, unsigned char nValue, unsigned char nLimit) {
	if (nValue > nLimit) {
		nValue -= nLimit;
	}
	else {
		nValue = nLimit - nValue;
		fMultiplier = 1.0f / fMultiplier;
	}
	fFrequency *= exp( nValue * log( fMultiplier ) );
	return fFrequency;
}


float Sonant_getnotefreq(unsigned char n) {
	return Sonant_getFrequency(0.00390625f, 1.059463094f, n, 128);
}

float Sonant_getoscoutput(unsigned char nWaveform,float fValue) {
	return (*(afpOscillator + nWaveform))(fValue);
}

/******************************************************************************
	End of file
******************************************************************************/
